﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Windows.Documents;
using TeamTasker.Utils;

namespace TeamTasker.Client.Presentation.Controls.SearchInTreeControl
{
    public class NodeModelHierarchicalCollection<T> : INodeModelHierarchicalCollection, IEnumerable<NodeModel>
    {
        protected readonly IList<NodeModel> _FirstLevelNodes;
        protected readonly Func<NodeModel, IList<NodeModel>> _NodeChildrenGetter = tn => tn.SubModels;
        protected readonly Action<NodeModel, NodeModel> _NodeParentSetter = (tn, parent) => tn.ParentModel = parent;

        protected NodeModelHierarchicalCollection()
        {
            _FirstLevelNodes = new List<NodeModel>();
        }

        public NodeModelHierarchicalCollection(IEnumerable<T> initialCollection,
                                               Func<T, IEnumerable<T>> sourceSubNodesGetter,
                                               Func<T, int, NodeModel> createNewNodeFunction)
            : this()
        {
            TreeHelper.FillInTree(initialCollection, _FirstLevelNodes, sourceSubNodesGetter,
                                  _NodeChildrenGetter, _NodeParentSetter, createNewNodeFunction);
        }

        public NodeModelHierarchicalCollection(IEnumerable initialCollection,
                                               Func<object, IEnumerable> sourceSubNodesGetter,
                                               Func<object, int, NodeModel> createNewNodeFunction)
            : this()
        {
            TreeHelper.FillInTree(initialCollection, _FirstLevelNodes, sourceSubNodesGetter, _NodeChildrenGetter,
                                  _NodeParentSetter, createNewNodeFunction);
        }

        #region IEnumerable<NodeModel> Members

        public IEnumerator<NodeModel> GetEnumerator()
        {
            return _FirstLevelNodes.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        #endregion

        #region INodeModelHierarchicalCollection Members

        public void FilterByNodeLabel(string textToSearch)
        {
            string text = textToSearch.ToLower();
            TreeHelper.SearchAllOccurences(_FirstLevelNodes, _NodeChildrenGetter,
                                           tn =>
                                           tn.PlainLabel.IndexOf(text, StringComparison.CurrentCultureIgnoreCase) != -1,
                                           tn =>
                                               {
                                                   SetVisible(tn);
                                                   SelectSubstringInLabel(tn, text);
                                               },
                                           tn =>
                                               {
                                                   tn.IsVisible = false;
                                                   ClearSelection(tn);
                                               });
        }

        public NodeModel FindNodeByData(object data)
        {
            return TreeHelper.SearchFirstOccurence(_FirstLevelNodes, _NodeChildrenGetter, tn => tn.Data == data);
        }

        #endregion

        private static void SelectSubstringInLabel(NodeModel node, String textToSearch)
        {
            if (!String.IsNullOrEmpty(textToSearch))
            {
                var paragraph = new Paragraph();
                InlineCollection content = paragraph.Inlines;
                string plainText = node.PlainLabel;
                int j = plainText.IndexOf(textToSearch, StringComparison.CurrentCultureIgnoreCase);
                while (j != -1)
                {
                    if (j != 0)
                    {
                        content.Add(plainText.Substring(0, j));
                    }
                    content.Add(new Bold(new Run(plainText.Substring(j, textToSearch.Length))));
                    plainText = plainText.Remove(0, j + textToSearch.Length);
                    j = plainText.IndexOf(textToSearch, StringComparison.InvariantCultureIgnoreCase);
                }
                if (plainText.Length != 0)
                {
                    content.Add(plainText);
                }
                node.Label.Blocks.Clear();
                node.Label.Blocks.Add(paragraph);
            }
            else
            {
                ClearSelection(node);
            }
        }

        private static void ClearSelection(NodeModel node)
        {
            string plainText = node.PlainLabel;
            node.Label.Blocks.Clear();
            node.Label.Blocks.Add(new Paragraph(new Run(plainText)));
        }

        private static void SetVisible(NodeModel currentNode)
        {
            currentNode.IsVisible = true;
            if (currentNode.ParentModel != null && !currentNode.ParentModel.IsVisible)
            {
                SetVisible(currentNode.ParentModel);
            }
        }
    }
}